﻿#ifndef __JS_FETCH_H__
#define __JS_FETCH_H__
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/bind.h>

using namespace emscripten;

#include <functional>
#include <string>
#include <iostream>

static std::unordered_map<std::string, std::function<void(std::string)>> *GetJsCallBackStringMap()
{
    static std::unordered_map<std::string, std::function<void(std::string)>> s_js_callback_string_map;
    return &s_js_callback_string_map;
}

static void RegisterJsCallBackString(const std::string &id, std::function<void(std::string)> callback)
{
    GetJsCallBackStringMap()->insert_or_assign(id, callback);
}

static void TriggerJsCallBackString(const std::string &id, const std::string &data)
{
    auto it = GetJsCallBackStringMap()->find(id);
    if (it != GetJsCallBackStringMap()->end())
    {
        it->second(data);
    }
}

static void HandleJsCallBackString(const std::string &id, const std::string &data)
{
    std::cout << "==>" << id << " " << data << std::endl;
    TriggerJsCallBackString(id, data);
}

class JsFetchResponse
{
public:
    JsFetchResponse() 
    {
        m_status_code = 0;
        m_finished = false;
    }
    ~JsFetchResponse() 
    {

    }
    std::string GetID() { return std::to_string(reinterpret_cast<uintptr_t>(this)); }
    void SetFinished(bool finished) { m_finished = finished; }
    void SetStatusCode(int status_code) { m_status_code = status_code; }
    void AppendHeader(const std::string& key, const std::string& value) { m_headers[key] = value; }
    void AppendData(const std::string& data) { m_datas.insert(m_datas.end(), data.begin(), data.end()); }

protected:
    int m_status_code;
    std::unordered_map<std::string, std::string> m_headers;    
    std::vector<unsigned char> m_datas;
    bool m_finished;
};


EMSCRIPTEN_BINDINGS(JsFetch)
{
    class_<JsFetchResponse>("JsFetchResponse")
        .smart_ptr_constructor("JsFetchResponse", &std::make_shared<JsFetchResponse>)
        .function("GetID", &JsFetchResponse::GetID)
        .function("SetStatusCode", &JsFetchResponse::SetStatusCode)
        .function("AppendHeader", &JsFetchResponse::AppendHeader)
        .function("AppendData", &JsFetchResponse::AppendData)
        .function("SetFinished", &JsFetchResponse::SetFinished);

}

static emscripten::val JSFetch()
{
    emscripten::val fetch = emscripten::val::global("jsfetch");
    // 创建请求配置对象
    emscripten::val options = emscripten::val::object();
    // 设置请求头部信息
    emscripten::val headers = emscripten::val::object();
    headers.set(emscripten::val("content-type"), emscripten::val("application/json"));
    headers.set(emscripten::val("Authorization"), emscripten::val("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjMsInVzZXJuYW1lIjoieGlhb3lhbyIsInBsYXRmb3JtIjoiTU9CSUxFIiwibWFjaGluZUNvZGUiOiIiLCJpYXQiOjE3MjIzMDA4NzZ9.MEVbSiP2ewKDFsuOf0mYgmjs8VYY98lgG0FpSEQT-6s"));
    options.set("headers", headers);
    // 设置请求方法
    options.set(emscripten::val("method"), emscripten::val("POST"));
    // 设置请求数据
    options.set(emscripten::val("body"), emscripten::val(reinterpret_cast<const char *>(u8R"({"messages":[{"content":"什么是帕拉卡","role":"user"}],"stream":true,"model":"ERNIE-Bot"})")));
    options.set(emscripten::val("responseType"), emscripten::val("blob"));
    // 发送请求
    emscripten::val response = co_await fetch(emscripten::val("https://api.keepwork.com/core/v0/gpt/chat"), options);
    std::string response_headers = response["headers"].as<std::string>();
    std::cout << "headers: " << response_headers << std::endl;
    bool is_stream = response["stream"].as<bool>();
    emscripten::val response_body = response["body"];
    while (is_stream)
    {
        emscripten::val response_body_value = co_await response_body();
        if (response_body_value["done"].as<bool>()) break;
        emscripten::val response_body_value_data = response_body_value["value"];
        std::string response_body_value_data_string = response_body_value_data.as<std::string>();
        std::cout << "body: " << response_body_value_data_string << std::endl;
    }

    std::cout << "js fetch finished" << std::endl;
}
#endif

#endif